package io.itit.ecp.admin.server.service.impl;

import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import icu.mhb.mybatisplus.plugln.base.service.impl.JoinServiceImpl;
import io.itit.ecp.admin.server.entity.*;
import io.itit.ecp.admin.server.mapper.DeviceMapper;
import io.itit.ecp.admin.server.mapper.MeterDeviceMapper;
import io.itit.ecp.admin.server.mapper.PhysicalModelMapper;
import io.itit.ecp.admin.server.mapper.ProductTypeMapper;
import io.itit.ecp.admin.server.service.IDeviceRunningHistoryService;
import io.itit.ecp.admin.server.service.IDeviceRunningValueService;
import io.itit.ecp.admin.server.service.IPhysicalModelService;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;


@Service
public class PhysicalModelServiceImpl extends JoinServiceImpl<PhysicalModelMapper, PhysicalModelEntity>
        implements IPhysicalModelService {

    @Resource
    private IDeviceRunningHistoryService deviceRunningHistoryService;

    @Resource
    private IDeviceRunningValueService deviceRunningValueService;

    @Resource
    private MeterDeviceMapper meterDeviceMapper;

    @Resource
    private DeviceMapper deviceMapper;

    @Resource
    private ProductTypeMapper productTypeMapper;

    @Override
    public BigDecimal queryValueByIdsAndDeviceIdAndName(String deviceId, String[] modelIds, String name, LocalDateTime start, LocalDateTime end) {
        Set<String> ids = ArrayUtil.isEmpty(modelIds) ? Collections.emptySet() : Arrays.stream(modelIds).collect(Collectors.toSet());
        List<PhysicalModelEntity> modelEntities = lambdaQuery()
                .select(PhysicalModelEntity::getId)
                .in(!ids.isEmpty(), PhysicalModelEntity::getId, ids)
                .eq(StringUtils.isNotEmpty(name), PhysicalModelEntity::getModelName, name)
                .list();
        Set<String> physicalModelIds = modelEntities.stream()
                .map(PhysicalModelEntity::getId)
                .collect(Collectors.toSet());
        if (physicalModelIds.isEmpty()) {
            return BigDecimal.ZERO;
        }
        // 这里先计算历史值，如果 io.itit.ecp.admin.common.enums.IPhysicalModelModelFutureEnums 对此枚举需要计算其它类型的值时，需要补充
        // 在保存运行参数时，只对历史数据进行了保存,参考: io.itit.ecp.admin.provide.action.controller.DeviceController.saveRunningParameter
        List<DeviceRunningHistoryEntity> list = deviceRunningHistoryService.lambdaQuery()
                .select(DeviceRunningHistoryEntity::getRunningValue)
                .in(DeviceRunningHistoryEntity::getPhysicalModelId, physicalModelIds)
                .ge(DeviceRunningHistoryEntity::getCreateTm, start)
                .lt(DeviceRunningHistoryEntity::getCreateTm, end)
                .list();
        BigDecimal result = BigDecimal.ZERO;
        for (DeviceRunningHistoryEntity historyEntity : list) {
            String runningValue = historyEntity.getRunningValue();
            result = result.add(getValue(runningValue));
        }

        // 静态数据保存的值
        if (StringUtils.isNotEmpty(deviceId)) {
            List<DeviceRunningValueEntity> runningValueEntities = deviceRunningValueService.lambdaQuery()
                    .select(DeviceRunningValueEntity::getRunningValue)
                    .eq(DeviceRunningValueEntity::getDeviceId, deviceId)
                    .in(DeviceRunningValueEntity::getPhysicalModelId, physicalModelIds)
                    .ge(Objects.nonNull(start), DeviceRunningValueEntity::getCreateTm, start)
                    .lt(Objects.nonNull(end), DeviceRunningValueEntity::getCreateTm, end).list();
            for (DeviceRunningValueEntity item : runningValueEntities) {
                result = result.add(getValue(item.getRunningValue()));
            }
        }
        return result.setScale(2, RoundingMode.HALF_UP);
    }



    private static BigDecimal getValue(String runningValue) {
        BigDecimal result = BigDecimal.ZERO;
        if (StringUtils.isNotEmpty(runningValue)) {
            String[] runningValues = runningValue.split(",");
            for (String value : runningValues) {
                try {
                    BigDecimal val = new BigDecimal(value);
                    result = result.add(val);
                } catch (Exception e) {
                    // it is not a BigDecimal type.
                }
            }
        }
        return result;
    }

    @Override
    public BigDecimal queryRunningDataValueDeviceCode(String deviceCode, String name, LocalDateTime start, LocalDateTime end) {
        DeviceEntity deviceEntity = deviceMapper.selectOne(new LambdaQueryWrapper<DeviceEntity>()
                .eq(DeviceEntity::getDeviceCode, deviceCode));
        if (Objects.isNull(deviceEntity)) {
            return BigDecimal.ZERO;
        }
        return queryRunningDataValueByProductTypeId(deviceEntity.getProductTypeId(), name, start, end);
    }


    @Override
    public BigDecimal queryRunningDataValue(String meterDeviceId, String name, LocalDateTime start, LocalDateTime end) {
        MeterDeviceEntity meterDeviceEntity = meterDeviceMapper.selectById(meterDeviceId);
        if (Objects.isNull(meterDeviceEntity)) {
            return BigDecimal.ZERO;
        }
        return queryRunningDataValueByProductTypeId(meterDeviceEntity.getProductTypeId(), name, start, end);
    }

    private BigDecimal queryRunningDataValueByProductTypeId(String productTypeId, String name, LocalDateTime start, LocalDateTime end) {
        ProductTypeEntity productTypeEntity = productTypeMapper.selectById(productTypeId);
        if (Objects.isNull(productTypeEntity)) {
            return BigDecimal.ZERO;
        }
        String productModelIds = productTypeEntity.getModelIds();
        String[] modelIdsArray = null;
        if (StringUtils.isNotEmpty(productModelIds)) {
            modelIdsArray = productModelIds.split(",");
        }
        if (Objects.isNull(modelIdsArray) || ArrayUtils.isEmpty(modelIdsArray)) {
            return BigDecimal.ZERO;
        }
        return queryValueByIdsAndName(modelIdsArray, name, start, end);
    }
}
